/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
  PdePreprocessor - wrapper for default ANTLR-generated parser
  Part of the Wiring project - http://wiring.org.co

  Copyright (c) 2004-05 Hernando Barragan

  Processing version Copyright (c) 2004-05 Ben Fry and Casey Reas
  Copyright (c) 2001-04 Massachusetts Institute of Technology

  ANTLR-generated parser and several supporting classes written
  by Dan Mosedale via funding from the Interaction Institute IVREA.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package processing.app.preproc;

import processing.app.*;
import processing.core.*;

import java.io.*;
import java.util.*;

import java.util.regex.*;


/**
 * Class that orchestrates Wiring syntax into straight C/C++.
 */
public class PdePreprocessor {
  /*
   *  ======== InoCode ========
   *  Simple nested class to simplify the conversion of
   *  source files into strings for parsing
   */
  static public class InoCode implements Comparable<InoCode> {
      private String source;
      private File file;
      private int lineCount;
      private int offset;
      private int priority;
      
      public InoCode(File file)
      {
          this(file, null, 0);
      }

      public InoCode(File file, String content, int lineCount)
      {
          this.file = file;
          this.source = content;
          this.lineCount = lineCount;
          this.offset = 0;
          this.priority = 1;
      }

      public int compareTo(InoCode n)
      {
          int cmp = this.priority - n.priority;
          if (cmp == 0) {
              return (this.file.getName().compareTo(n.file.getName()));
          }
          return (cmp);
      }

      public String getProgram() throws Exception
      {
          if (source == null) {
              BufferedReader reader = new BufferedReader(
                  new FileReader (file.getAbsolutePath()));
              String         line = null;
              StringBuilder  stringBuilder = new StringBuilder();
              String         nl = System.getProperty("line.separator");

              while ((line = reader.readLine()) != null) {
                  stringBuilder.append(line);
                  stringBuilder.append(nl);
                  lineCount++;
              }
              reader.close();
          
              source = stringBuilder.toString();
          }
          return (source);
      }

      public String getFileName()
      {
          return (file.getName());
      }

      public String getFullPath()
      {
          try {
              return (file.getCanonicalPath());
          }
          catch (Exception e) {
              return (file.getAbsolutePath());
          }
      }

      public int getLineCount()
      {
          return (lineCount);
      }

      public int getOffset()
      {
          return (offset);
      }

      public void setPri(int pri)
      {
          priority = pri;
      }
      
      public void setOffset(int offset)
      {
          this.offset = offset;
      }
  }

  // stores number of built user-defined function prototypes
  public int prototypeCount = 0;

  // stores number of included library headers written
  // we always write one header: Arduino.h
  public int headerCount = 1;
  
  // the prototypes that are generated by the preprocessor
  List<String> prototypes;

  // these ones have the .* at the end, since a class name might be at the end
  // instead of .* which would make trouble other classes using this can lop
  // off the . and anything after it to produce a package name consistently.
  List<String> programImports;

  // imports just from the code folder, treated differently
  // than the others, since the imports are auto-generated.
  List<String> codeFolderImports;

  String indent;

  PrintStream stream;
  String program;
  String buildPath;
  // starts as sketch name, ends as main class name
  String name;


  /**
   * Setup a new preprocessor.
   */
  public PdePreprocessor() { 
    int tabSize = Preferences.getInteger("editor.tabs.size");
    char[] indentChars = new char[tabSize];
    Arrays.fill(indentChars, ' ');
    indent = new String(indentChars);
  }

  /**
   * Writes out the head of the c++ code generated for a sketch. 
   * Called from processing.app.Sketch.
   * @param program the concatenated code from all tabs containing pde-files
   * @param buildPath the path into which the processed pde-code is to be written
   * @param name the name of the sketch 
   * @param codeFolderPackages unused param (leftover from processing)
   */
  public int writePrefix(String program, String buildPath,
                         String sketchName, String codeFolderPackages[]) throws FileNotFoundException {
    this.buildPath = buildPath;
    this.name = sketchName;

    // if the program ends with no CR or LF an OutOfMemoryError will happen.
    // not gonna track down the bug now, so here's a hack for it:
    // http://dev.processing.org/bugs/show_bug.cgi?id=5
    program += "\n";

    // if the program ends with an unterminated multi-line comment,
    // an OutOfMemoryError or NullPointerException will happen.
    // again, not gonna bother tracking this down, but here's a hack.
    // http://dev.processing.org/bugs/show_bug.cgi?id=16
    Sketch.scrubComments(program);
    // If there are errors, an exception is thrown and this fxn exits.

    if (Preferences.getBoolean("preproc.substitute_unicode")) {
      program = substituteUnicode(program);
    }

    //String importRegexp = "(?:^|\\s|;)(import\\s+)(\\S+)(\\s*;)";
    String importRegexp = "^\\s*#include\\s*[<\"](\\S+)[\">]";
    programImports = new ArrayList<String>();

    String[][] pieces = matchAll(program, importRegexp);

    if (pieces != null)
      for (int i = 0; i < pieces.length; i++)
        programImports.add(pieces[i][1]);  // the package name

    codeFolderImports = new ArrayList<String>();
//    if (codeFolderPackages != null) {
//      for (String item : codeFolderPackages) {
//        codeFolderImports.add(item + ".*");
//      }
//    }

    prototypes = prototypes(program);
    
    // store # of prototypes so that line number reporting can be adjusted
    prototypeCount = prototypes.size();
  
    // do this after the program gets re-combobulated
    this.program = program;
    
    // generate main.cpp (it depends on what's in the .ino files)
    if (Base.getArch() == "cc3200emt"
        || Base.getArch() == "msp432"
        || Base.getArch() == "msp432e"
        || Base.getArch() == "cc2600emt") {

        String template = Preferences.get("preproc.main.template");
    	writemain(buildPath, template);
    }
    
    // output the code
    File streamFile = new File(buildPath, name + ".cpp");
    stream = new PrintStream(new FileOutputStream(streamFile));
    
    return headerCount + prototypeCount;
  }


  static String substituteUnicode(String program) {
    // check for non-ascii chars (these will be/must be in unicode format)
    char p[] = program.toCharArray();
    int unicodeCount = 0;
    for (int i = 0; i < p.length; i++) {
      if (p[i] > 127) unicodeCount++;
    }
    // if non-ascii chars are in there, convert to unicode escapes
    if (unicodeCount != 0) {
      // add unicodeCount * 5.. replacing each unicode char
      // with six digit uXXXX sequence (xxxx is in hex)
      // (except for nbsp chars which will be a replaced with a space)
      int index = 0;
      char p2[] = new char[p.length + unicodeCount*5];
      for (int i = 0; i < p.length; i++) {
        if (p[i] < 128) {
          p2[index++] = p[i];

        } else if (p[i] == 160) {  // unicode for non-breaking space
          p2[index++] = ' ';

        } else {
          int c = p[i];
          p2[index++] = '\\';
          p2[index++] = 'u';
          char str[] = Integer.toHexString(c).toCharArray();
          // add leading zeros, so that the length is 4
          //for (int i = 0; i < 4 - str.length; i++) p2[index++] = '0';
          for (int m = 0; m < 4 - str.length; m++) p2[index++] = '0';
          System.arraycopy(str, 0, p2, index, str.length);
          index += str.length;
        }
      }
      program = new String(p2, 0, index);
    }
    return program;
  }

  /**
   * preprocesses a pde file and writes out a java file
   * @return the classname of the exported Java
   */
  //public String write(String program, String buildPath, String name,
  //                  String extraImports[]) throws java.lang.Exception {
  public String write() throws java.lang.Exception {
    try {
      writeProgram(stream, program, prototypes);
      writeFooter(stream);
    }
    finally { /* make sure stream is closed */
      if (stream != null) {
        stream.close();
      }
    }
    return name;
  }

  // Write the pde program to the cpp file
  protected void writeProgram(PrintStream out, String program, List<String> prototypes) {
    /* find appropriate location for the setup/loop declarations */
    int prototypeInsertionPoint = firstStatement(program);
  
    /* output everything up to that point */
    out.print(program.substring(0, prototypeInsertionPoint));

    /* insert standard #include's */
    String arch = Base.getArch();
    if (arch == "msp430") out.print("#include \"Energia.h\"\n");    
    else out.print("#include \"Arduino.h\"\n");    
    
    /* insert user defined prototypes */
    for (int i = 0; i < prototypes.size(); i++) {
      out.print(prototypes.get(i) + "\n");
    }

    /* count # of lines between last "#line 1 " and insertion point */
    String[] lines = program.substring(0, prototypeInsertionPoint).split("\n", -1);
    int line = 1;
    for (int i = 0; i < lines.length; i++) {
      if (lines[i].startsWith("#line 1 ")) {
        line = 1;
      }
      else {
        line++;
      }
    }
    
    /* output a #line directive to ignore the lines inserted above */
    out.println("#line " + (line - 1));

    /* output the remainder of program */
    out.print(program.substring(prototypeInsertionPoint));
  }


  /**
   * Write any necessary closing text.
   *
   * @param out         PrintStream to write it to.
   */
  protected void writeFooter(PrintStream out) throws java.lang.Exception {}


  public List<String> getExtraImports() {
    return programImports;
  }

  /*
   *  ======== sortInoCode ========
   */
  private static void sortInoCode(ArrayList<InoCode> code, String sketchName)
  {
      /* set code element priorities */
      for (InoCode ic : code) {
          /* the .ino that matches the sketch name, must come first */
          if (ic.getFileName().equals(sketchName)) {
              ic.setPri(0);  
          }
      }

      /* sort by priority then alphabetically */
      Collections.sort(code);
  }
  
  /*
   *  ======== generate ========
   *  buildPath  - the output directory for the preprocessor's generated
   *               files
   *  code       - an array of all input .ino source code
   *  sketchName - the name of the sketch project
   *  isEMT      - generate code to support multiple setup/loop pairs
   */

  public String generate(String buildPath, ArrayList<InoCode> code,
                         String sketchName, boolean isEMT)
  {
    String primaryClassName = null;
    try {
        primaryClassName = generate(buildPath, code, sketchName, null, isEMT);
    }
    catch (Exception e) {
          System.err.println(e);
          e.printStackTrace();
    }
    return primaryClassName;
  }

  public String generate(String buildPath, ArrayList<InoCode> code,
                         String sketchName, String aSketchName, boolean isEMT)
      throws Exception
  {
      // 0. sort code into canonical order
      sortInoCode(code, sketchName + ".ino");
      
      // 1. concatenate all .ino files to the 'main' .cpp
      //    store line number for starting point of each code bit
      StringBuffer bigCode = new StringBuffer();
      int curOffset = 0;
      for (InoCode isc : code) {
          String in = isc.getProgram();
          isc.setOffset(curOffset);
          
          /* if EMT we need to handle multiple *Loop and *Setup functions */
          if (isEMT) {
	
              // Find all loop functions and generate prototypes for them
              ArrayList<String> loopMatches = new ArrayList<String>();
		    	    
              Pattern functionPattern  = Pattern.compile("\\s*void\\s+(loop)\\s*\\(\\s*(void)?\\s*\\)");
              Matcher functionMatcher = functionPattern.matcher(in);
              while (functionMatcher.find()) {
                  loopMatches.add(functionMatcher.group(1));
              }
	
              // Find all setup functions and generate prototypes for them
              ArrayList<String> setupMatches = new ArrayList<String>();

              functionPattern = Pattern.compile("\\s*void\\s+(setup)\\s*\\(\\s*(void)?\\s*\\)");
              functionMatcher = functionPattern.matcher(in);
              while (functionMatcher.find()) {
                  setupMatches.add(functionMatcher.group(1));
              }
	        
              // Add #line directives to help the compiler report errors with
              // correct the filename and line number (issue 281 & 907)
              if (setupMatches.size() > 0 && loopMatches.size() > 0) {
                  /* map setup and loop to an .ino qualified name to allow
                   * blind copy existing .ino files, each with a setup/loop 
                   * pair, into a project
                   */
                  String inoName = isc.getFileName().substring(0, isc.getFileName().length()-4);
                  bigCode.append("#undef setup\n#undef loop\n");
                  bigCode.append("#define setup setup" +  inoName + "\n");
                  bigCode.append("#define loop loop" +  inoName + "\n");
              }
              bigCode.append("#line 1 \"" + isc.getFullPath().replace("\\", "\\\\") + "\"\n");
          }
      
          bigCode.append(in);
          bigCode.append('\n');
          curOffset += isc.getLineCount();
      }

      // 2. generate main.cpp and the first part of the sketch.cpp;
      //    i.e., up to the concatenated *.ino content
      int prefixLen = 0;
      sketchName = aSketchName == null ? sketchName:aSketchName;

      try {
          prefixLen = writePrefix(bigCode.toString(),
                                   buildPath,
                                   sketchName,
                                   null);
      }
      catch (FileNotFoundException fnfe) {
          fnfe.printStackTrace();
          String msg = "Build folder disappeared or could not be written";
          throw new Exception(msg);
      }

      for (InoCode isc : code) {
          /* update offsets based on initial prefix */
          isc.setOffset(isc.getOffset() + prefixLen);
      }

      // 3. run preproc on that code using the suggested class name
      //    to create a single .cpp file and write to buildPath
      String primaryClassName = null;
      try {
          // if (i != 0) preproc will fail if a pde file is not
          // java mode, since that's required
          String className = write();
          if (className == null) {
              throw new Exception("Could not find main class");
          }

          // store this for the compiler and the runtime
          primaryClassName = className + ".cpp";

      }
      catch (FileNotFoundException fnfe) {
          fnfe.printStackTrace();
          String msg = "Build folder disappeared or could not be written";
          throw new Exception(msg);
      }
      catch (Exception ex) {
          // TODO better method for handling this?
          ex.printStackTrace();
          throw new Exception(ex.toString());
      }

      return primaryClassName;
  }


  /**
   * Returns the index of the first character that's not whitespace, a comment
   * or a pre-processor directive.
   */
  public int firstStatement(String in) {
    // whitespace
    String p = "\\s+";
    
    // multi-line and single-line comment
    //p += "|" + "(//\\s*?$)|(/\\*\\s*?\\*/)";
    p += "|(/\\*[^*]*(?:\\*(?!/)[^*]*)*\\*/)|(//.*?$)";

    // pre-processor directive
    p += "|(#(?:\\\\\\n|.)*)";
    Pattern pattern = Pattern.compile(p, Pattern.MULTILINE);
      
    Matcher matcher = pattern.matcher(in);
    int i = 0;
    while (matcher.find()) {
      if (matcher.start()!=i)
        break;
      i = matcher.end();
    }
    
    return i;
  }
  
  /**
   * Strips comments, pre-processor directives, single- and double-quoted
   * strings from a string.
   * @param in the String to strip
   * @return the stripped String
   */
  public String strip(String in) {
    // XXX: doesn't properly handle special single-quoted characters
    // single-quoted character
    String p = "('.')";
    
    // double-quoted string
    p += "|(\"(?:[^\"\\\\]|\\\\.)*\")";
    
    // single and multi-line comment
    //p += "|" + "(//\\s*?$)|(/\\*\\s*?\\*/)";
    p += "|(//.*?$)|(/\\*[^*]*(?:\\*(?!/)[^*]*)*\\*/)";
    
    // pre-processor directive
    p += "|" + "(^\\s*#.*?$)";

    Pattern pattern = Pattern.compile(p, Pattern.MULTILINE);
    Matcher matcher = pattern.matcher(in);
    return matcher.replaceAll(" ");
  }
  
  /**
   * Removes the contents of all top-level curly brace pairs {}.
   * @param in the String to collapse
   * @return the collapsed String
   */
  private String collapseBraces(String in) {
    StringBuffer buffer = new StringBuffer();
    int nesting = 0;
    int start = 0;
    
    // XXX: need to keep newlines inside braces so we can determine the line
    // number of a prototype
    for (int i = 0; i < in.length(); i++) {
      if (in.charAt(i) == '{') {
        if (nesting == 0) {
          buffer.append(in.substring(start, i + 1));  // include the '{'
        }
        nesting++;
      }
      if (in.charAt(i) == '}') {
        nesting--;
        if (nesting == 0) {
          start = i; // include the '}'
        }
      }
    }
    
    buffer.append(in.substring(start));
    
    return buffer.toString();
  }
  
  /**
   * Find all matches of a regexp in the specified string
   */
  static private String[][] matchAll(String what, String regexp) {
    Pattern p = Pattern.compile(regexp, Pattern.MULTILINE | Pattern.DOTALL);
    Matcher m = p.matcher(what);
    ArrayList<String[]> results = new ArrayList<String[]>();
    int count = m.groupCount() + 1;
    while (m.find()) {
      String[] groups = new String[count];
      for (int i = 0; i < count; i++) {
        groups[i] = m.group(i);
      }
      results.add(groups);
    }
    if (results.isEmpty()) {
      return null;
    }
    String[][] matches = new String[results.size()][count];
    for (int i = 0; i < matches.length; i++) {
      matches[i] = (String[]) results.get(i);
    }
    return matches;
  }

  public ArrayList<String> prototypes(String in) {
    in = collapseBraces(strip(in));
    
    // XXX: doesn't handle ... varargs
    // XXX: doesn't handle function pointers
    Pattern prototypePattern = Pattern.compile("[\\w\\[\\]\\*]+\\s+[&\\[\\]\\*\\w\\s]+\\([&,\\[\\]\\*\\w\\s]*\\)(?=\\s*;)");
    Pattern functionPattern  = Pattern.compile("[\\w\\[\\]\\*]+\\s+[&\\[\\]\\*\\w\\s]+\\([&,\\[\\]\\*\\w\\s]*\\)(?=\\s*\\{)");
    
    // Find already declared prototypes
    ArrayList<String> prototypeMatches = new ArrayList<String>();
    Matcher prototypeMatcher = prototypePattern.matcher(in);
    while (prototypeMatcher.find())
      prototypeMatches.add(prototypeMatcher.group(0) + ";");
    
    // Find all functions and generate prototypes for them
    ArrayList<String> functionMatches = new ArrayList<String>();
    Matcher functionMatcher = functionPattern.matcher(in);
    while (functionMatcher.find())
      functionMatches.add(functionMatcher.group(0) + ";");
    
    // Remove generated prototypes that exactly match ones found in the source file
    for (int functionIndex=functionMatches.size() - 1; functionIndex >= 0; functionIndex--) {
      for (int prototypeIndex=0; prototypeIndex < prototypeMatches.size(); prototypeIndex++) {
        if ((functionMatches.get(functionIndex)).equals(prototypeMatches.get(prototypeIndex))) {
          functionMatches.remove(functionIndex);
          break;
        }
      }
    }
    
    return functionMatches;
  }
  
  /**
   * Generate main.cpp from optionally specified template file and 
   * write result into outDir
   */
  private void writemain(String outDir, String template) {
      String _in = program;
      String in = collapseBraces(strip(program));
      
      String content = "";
      try {
          if (template == null) {
              template = Base.getHardwarePath() + File.separator + Base.getArch()
                  + File.separator + "cores" + File.separator + Base.getArch()
                  + File.separator + "main.template";
          }
          content = new Scanner(new File(template)).useDelimiter("\\Z").next();
          //System.out.println(content);
      } catch (FileNotFoundException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      }

      StringBuilder mainFile = new StringBuilder(content);
  	
      int insertionPoint = content.indexOf("769d20fcd7a0eedaf64270f591438b01");
      insertionPoint = content.indexOf("\n", insertionPoint) + 1;

      //Pattern functionPattern  = Pattern.compile("(?=([\\w\\[\\]\\*]+\\s+[&\\[\\]\\*\\w\\s])*)\\w*[Ll]oop(?=\\s*\\()");
      Pattern functionPattern  = Pattern.compile("\\s*void\\s+([a-zA-Z_]*[lL]oop\\w*)\\s*\\(\\s*(void)?\\s*\\)");

      // Find all functions and generate prototypes for them
      ArrayList<String> loopMatches = new ArrayList<String>();
      ArrayList<String> setupMatches = new ArrayList<String>();
      	    
      Matcher functionMatcher = functionPattern.matcher(in);

      // Leave setup alone since it is special
      // It will be processed using the next regex
      // See Sketch.java for the generation of the #define setupxxx
      while (functionMatcher.find()) {
      	String func = functionMatcher.group(1);
      	if(func.equals("loop")) continue;
      	loopMatches.add(functionMatcher.group(1));
      }
      
      //functionPattern  = Pattern.compile("(?=([\\w\\[\\]\\*]+\\s+[&\\[\\]\\*\\w\\s])*)\\w*[Ss]etup(?=\\s*\\()");
      functionPattern  = Pattern.compile("\\s*void\\s+([a-zA-Z_]*[sS]etup\\w*)\\s*\\(\\s*(void)?\\s*\\)");

      // Find all functions and generate prototypes for them
      functionMatcher = functionPattern.matcher(in);
      
      // Leave setup alone since it is special
      // It will be processed using the next regex
      // See Sketch.java for the generation of the #define setupxxx
      while (functionMatcher.find()) {
      	String func = functionMatcher.group(1);
      	if(func.equals("setup")) continue;
      	setupMatches.add(functionMatcher.group(1));
      }

      functionPattern  = Pattern.compile("(#define setup\\s)([a-zA-Z_*]\\w*)");

      // Find all functions and generate prototypes for them
      functionMatcher = functionPattern.matcher(_in);
      
      while (functionMatcher.find()) {
      	String func = functionMatcher.group(2);
      	setupMatches.add(func);
      }

      functionPattern  = Pattern.compile("(#define loop\\s)([a-zA-Z_*]\\w*)");

      // Find all functions and generate prototypes for them
      functionMatcher = functionPattern.matcher(_in);
      
      while (functionMatcher.find()) {
      	String func = functionMatcher.group(2);
      	loopMatches.add(func);
      }
      
      if(setupMatches.size() != loopMatches.size()) {
      	System.out.println("The number of loop functions does not match the number of setup functions\n" +
      			"Missing a loop or a setup in your Sketches?");
      	return;
      }

      String protos = "";
      String funcArray = "";
      String taskNameArray = "";
      
      for (int functionIndex = 0; functionIndex < loopMatches.size(); functionIndex++) {
      	protos += "extern void " + setupMatches.get(functionIndex) + "();\n";
      	protos += "extern void " + loopMatches.get(functionIndex) + "();\n";
      	funcArray += "\t{" + setupMatches.get(functionIndex) + ", " + loopMatches.get(functionIndex) + "}";
      	taskNameArray += "\t\"" + loopMatches.get(functionIndex) + "\"";
      	if(functionIndex < loopMatches.size() -1) {
      		funcArray += ",\n";
      		taskNameArray += ",\n";
      	}
      }

      String numSketches = "\n#define NUM_SKETCHES " + loopMatches.size() + "\n";
      String prolog = "void (*func_ptr[NUM_SKETCHES][2])(void) = {\n";
      String epilog = "\n};\n";
      mainFile.insert(insertionPoint, protos);
      insertionPoint += protos.length();
      mainFile.insert(insertionPoint, numSketches);
      insertionPoint += numSketches.length();
      mainFile.insert(insertionPoint, prolog);
      insertionPoint += prolog.length();	    
      mainFile.insert(insertionPoint, funcArray);
      insertionPoint += funcArray.length();
      mainFile.insert(insertionPoint, epilog);
      insertionPoint += epilog.length();

      prolog = "const char *taskNames[] = {\n";
      mainFile.insert(insertionPoint, prolog);
      insertionPoint += prolog.length();	    
      mainFile.insert(insertionPoint, taskNameArray);
      insertionPoint += taskNameArray.length();
      mainFile.insert(insertionPoint, epilog);
      
      try {
            String dirName = (outDir == null) ? Base.getBuildFolder().toString() : outDir;
            PrintWriter out = new PrintWriter(dirName + File.separator + "main.cpp");
  		out.print(mainFile.toString());
  		out.close();
  	} catch (FileNotFoundException e) {
  		// TODO Auto-generated catch block
  		e.printStackTrace();
  	}

      // Remove generated prototypes that exactly match ones found in the source file
//	    for (int functionIndex=functionMatches.size() - 1; functionIndex >= 0; functionIndex--) {
//	      for (int prototypeIndex=0; prototypeIndex < prototypeMatches.size(); prototypeIndex++) {
//	        if ((functionMatches.get(functionIndex)).equals(prototypeMatches.get(prototypeIndex))) {
//	          functionMatches.remove(functionIndex);
//	          break;
//	        }
//	      }
//	    }
      
      //return functionMatches;
    }
}
